package com.gee.spring.cloud.feign.flow.handler;

import com.gee.spring.cloud.feign.flow.exception.SkippedException;
import com.gee.spring.cloud.feign.flow.execute.ExecuteContext;
import com.gee.spring.cloud.feign.flow.execute.WorkExecuteProperties;
import com.gee.spring.cloud.feign.flow.execute.nextwork.NextWorkWrapper;
import com.gee.spring.cloud.feign.flow.result.CheckResult;
import com.gee.spring.cloud.feign.flow.result.ExecuteState;
import com.gee.spring.cloud.feign.flow.result.WorkResult;
import com.gee.spring.cloud.feign.flow.work.AbstractWork;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * desc:
 *
 * @author gee wrote on  2021-01-14 19:06:41
 */
public interface WorkHandler {

    Logger LOGGER = LoggerFactory.getLogger(WorkHandler.class);

    String WHOLE_SKIPPED_CAUSED_BY = "The whole thread flow has been exceptional caused by the work which expects,named ";

    String PREVIOUS_NECESSARY_EXCEPTION = "previous necessary work determined to skip  which named ";

    String ALL_PREVIOUS_UNNECESSARY_EXCEPTION = "all previous unnecessary works determined to skip:  ";

    String FOLLOW_UP_HAS_BEGUN = "the follow-up work has begun which is named ";

    String WHOLE_FUTURE_EXCEPTION = "the whole future executes exceptionally";

    CheckResult check(AbstractWork<?> currentWork, ExecuteContext executeContext);

    void handleBeforeBegin(AbstractWork<?> currentWork, ExecuteContext executeContext);

    void handleAfterFinish(AbstractWork<?> currentWork, ExecuteContext executeContext);

    default void trySkipPrevious(AbstractWork<?> currentWork, SkippedException skippedException,
                                 ExecuteContext executeContext) {
        WorkResult<?> preWorkResult = executeContext.getWorkResult(currentWork.getId());
        AtomicInteger executeState = preWorkResult.getExecuteState();
        //set the previous works's execute state from INIT to be EXCEPTIONAL to void execute
        WorkExecuteProperties<?> preWorkExecuteProperties = currentWork.getWorkExecuteProperties();
        if (executeState.compareAndSet(ExecuteState.INIT.getCode(), ExecuteState.EXCEPTIONAL.getCode())) {
            preWorkResult.setException(skippedException);
            ((WorkResult) preWorkResult).setExceptionMsg(skippedException.getMessage());
        /*
            set the previous works's execute state from WORKING to be EXCEPTIONAL and interrupt the thread
            if enableInterrupted
         */
        } else if (executeState.compareAndSet(ExecuteState.WORKING.getCode(), ExecuteState.EXCEPTIONAL.getCode())) {
            preWorkResult.setException(skippedException);
            preWorkResult.setExceptionMsg(skippedException.getMessage());
        }
        if (preWorkExecuteProperties.getPreWorks().size() > 0) {
            preWorkExecuteProperties.getPreWorks().forEach((key, work) -> {
                Map<String, ? extends NextWorkWrapper<?>> nextWorkWrapperMap = work.getWorkExecuteProperties().getNextWorks();
                if (allNextBegun(nextWorkWrapperMap, executeContext)) {
                    trySkipPrevious(work, skippedException, executeContext);
                }
            });
        }
    }

    default boolean allNextBegun(Map<String, ? extends NextWorkWrapper<?>> nextWorkWrapperMap, ExecuteContext executeContext) {
        if (nextWorkWrapperMap == null || nextWorkWrapperMap.size() == 0) {
            return true;
        }
        return nextWorkWrapperMap.keySet().parallelStream().allMatch(nextWorkId ->
                executeContext.getWorkResult(nextWorkId).getExecuteState().get() > ExecuteState.INIT.getCode()
        );
    }
}